﻿#region using
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Schema;

using XmlGenSharp.Logic.Main.Configuration;
#endregion

namespace XmlGenSharp.Logic.Main
{
  public class BindingGenerator : GeneratorBase
  {
    // Constructors

    #region BindingGenerator()
    public BindingGenerator() : this(string.Empty, "NotSpecified")
    {
    }
    #endregion

    #region BindingGenerator(string inputFileName, string defaultNamespace)
    public BindingGenerator(string inputFileName, string defaultNamespace)
      : base(inputFileName, defaultNamespace)
    {
    }
    #endregion

    // Public Properties

    #region ClassGenerator
    public ClassGenerator ClassGenerator
    {
      get;
      protected set;
    }
    #endregion

    #region EnumGenerator
    public EnumGenerator EnumGenerator
    {
      get;
      protected set;
    }
    #endregion

    #region Schema
    public XmlSchema Schema
    {
      get;
      private set;
    }
    #endregion

    #region SchemaSet
    public XmlSchemaSet SchemaSet
    {
      get;
      private set;
    }
    #endregion

    #region Job
    public Job Job
    {
      get;
      private set;
    }
    #endregion

    #region Namespace
    public string Namespace
    {
      get
      {
        if (Job != null && Job.CLRInfo != null && Job.CLRInfo.Namespace != null)
          return Job.CLRInfo.Namespace;

        return DefaultNamespace;
      }
    }
    #endregion

    #region Config
    public Config Config
    {
      get;
      protected set;
    }
    #endregion

    // Public Methods

    #region Generate(IGeneratorContext cx)
    public override void Generate(IGeneratorContext cx)
    {
      LoadConfig();

      if (!File.Exists(InputFileName))
      {
        cx.Writer.WriteLine("// File '{0}' not found", InputFileName);
        return;
      }

      if (Path.GetExtension(InputFileName).ToLower() == ".xsd")
        GenerateFromXsd(cx);
      else if (Path.GetExtension(InputFileName).ToLower() == ".xenum")
        GenerateFromEnumerationInfo(cx);
      else
        GenerateFromJobFile(cx);
    }
    #endregion

    #region GenerateFromJobFile(IGeneratorContext cx)
    public void GenerateFromJobFile(IGeneratorContext cx)
    {
      try
      {
        Job = new Job(InputFileName);

        string fullPath = Job.Schema.File;
        if (!Path.IsPathRooted(fullPath))
          fullPath = Path.Combine(Path.GetDirectoryName(InputFileName), fullPath);

        cx.Writer.WriteLine("#region Code Generation Information");
        cx.Writer.WriteLine("// This file was auto-generated by XmlGen#");
        cx.Writer.WriteLine("// DO NOT DIRECTLY MODIFY THIS FILE.  Changes will be overwritten");
        cx.Writer.WriteLine("// the next time this file is generated.");
        cx.Writer.WriteLine();
        cx.Writer.WriteLine("// XmlGen# Job File Name: {0}", InputFileName);
        cx.Writer.WriteLine("// Schema File Name:      {0}", fullPath);
        cx.Writer.WriteLine("// CLR Namespace:         {0}", Namespace);
        cx.Writer.WriteLine("// Generated On:          {0}", DateTime.Now);
        cx.Writer.WriteLine("#endregion");
        cx.Writer.WriteLine();

        XmlTextReader reader = new XmlTextReader(fullPath);
        Schema = XmlSchema.Read(reader, ValidationCallback);
        SchemaSet = new XmlSchemaSet();
        SchemaSet.Add(Schema);
        SchemaSet.Compile();

        Generate(cx);
      }
      catch (Exception ex)
      {
        cx.Writer.WriteLine("// Could not load job from file '{0}'", InputFileName);
        cx.Writer.WriteLine("// {0}", ex.Message);
      }
    }
    #endregion

    #region GenerateFromXsd(IGeneratorContext cx)
    public void GenerateFromXsd(IGeneratorContext cx)
    {
      try
      {
        XmlTextReader reader = new XmlTextReader(InputFileName);
        Schema = XmlSchema.Read(reader, ValidationCallback);
        SchemaSet = new XmlSchemaSet();
        SchemaSet.Add(Schema);
        SchemaSet.Compile();

        cx.Writer.WriteLine("#region Code Generation Information");
        cx.Writer.WriteLine("// This file was auto-generated by XmlGen#");
        cx.Writer.WriteLine("// DO NOT DIRECTLY MODIFY THIS FILE.  Changes will be overwritten");
        cx.Writer.WriteLine("// the next time this file is generated.");
        cx.Writer.WriteLine();
        cx.Writer.WriteLine("// Schema File Name: {0}", InputFileName);
        cx.Writer.WriteLine("// Generated On:     {0}", DateTime.Now);
        cx.Writer.WriteLine("#endregion");
        cx.Writer.WriteLine();

        Generate(cx);
      }
      catch (Exception ex)
      {
        cx.Writer.WriteLine("// Error loading XML schema file: '{0}'", InputFileName);
        cx.Writer.WriteLine("// {0}", ex.Message);
      }
    }
    #endregion

    #region GenerateFromEnumerationInfo(IGeneratorContext cx)
    public void GenerateFromEnumerationInfo(IGeneratorContext cx)
    {
      try
      {
        EnumerationInfo enumInfo = new EnumerationInfo(InputFileName);
        AdvancedEnumGenerator aeg = new AdvancedEnumGenerator(InputFileName, DefaultNamespace, enumInfo);

        cx.Writer.WriteLine("#region Code Generation Information");
        cx.Writer.WriteLine("// This file was auto-generated by XmlGen#");
        cx.Writer.WriteLine("// DO NOT DIRECTLY MODIFY THIS FILE.  Changes will be overwritten");
        cx.Writer.WriteLine("// the next time this file is generated.");
        cx.Writer.WriteLine();
        cx.Writer.WriteLine("// XmlGen# Enumeration File Name: {0}", InputFileName);
        cx.Writer.WriteLine("// CLR Namespace:                 {0}", Namespace);
        cx.Writer.WriteLine("// Generated On:                  {0}", DateTime.Now);
        cx.Writer.WriteLine("#endregion");
        cx.Writer.WriteLine();

        cx.Writer.WriteLine("#region using");
        cx.Writer.WriteLine("using System;");
        cx.Writer.WriteLine("using System.Collections;");
        cx.Writer.WriteLine("using System.Collections.Generic;");
        cx.Writer.WriteLine("using System.Xml;");
        cx.Writer.WriteLine();
        cx.Writer.WriteLine("using XmlGenSharp.Lib.Utility;");
        cx.Writer.WriteLine("#endregion");
        cx.Writer.WriteLine();
        cx.Writer.WriteLine("#region pragmas");
        cx.Writer.WriteLine("#pragma warning disable 0219");
        cx.Writer.WriteLine("#endregion");
        cx.Writer.WriteLine();
        cx.Writer.WriteLine("namespace {0}", Namespace);
        cx.Writer.Write("{");

        aeg.Generate(cx);

        cx.Writer.WriteLine("}");
      }
      catch (Exception ex)
      {
        cx.Writer.WriteLine("// Could not load job from file '{0}'", InputFileName);
        cx.Writer.WriteLine("// {0}", ex.Message);
      }
    }
    #endregion

    #region GetFileName()
    public override string GetFileName()
    {
      return null;
    }
    #endregion

    // Private Methods

    #region LoadConfig()
    private void LoadConfig()
    {
      string dir = Path.GetDirectoryName(InputFileName);
      string configFile = Path.Combine(dir, "XmlGenSharp.config");

      if (!File.Exists(configFile))
      {
        Config = new Config();
        return;
      }

      try
      {
        Config = new Config(configFile);
      }
      catch
      {
      }
    }
    #endregion

    #region GenerateAll(IGeneratorContext cx)
    private void GenerateAll(IGeneratorContext cx)
    {
      try
      {
        cx.Writer.WriteLine("#region using");
        cx.Writer.WriteLine("using System;");
        cx.Writer.WriteLine("using System.Collections;");
        cx.Writer.WriteLine("using System.Collections.Generic;");
        cx.Writer.WriteLine("using System.Xml;");
        cx.Writer.WriteLine();
        cx.Writer.WriteLine("using XmlGenSharp.Lib.Utility;");
        cx.Writer.WriteLine("#endregion");
        cx.Writer.WriteLine();
        cx.Writer.WriteLine("#region pragmas");
        cx.Writer.WriteLine("#pragma warning disable 0219");
        cx.Writer.WriteLine("#endregion");
        cx.Writer.WriteLine();
        cx.Writer.WriteLine("namespace {0}", Namespace);
        cx.Writer.Write("{");

        foreach (XmlSchemaObject obj in Schema.SchemaTypes.Values)
        {
          try
          {
            if (obj is XmlSchemaComplexType)
              ClassGenerator.Generate(cx);
            // else if (obj is XmlSchemaSimpleType)
              // EnumGenerator.Generate(obj as XmlSchemaSimpleType);
          }
          catch (Exception ex)
          {
            cx.Writer.WriteLine("// {0}", ex.Message);
          }
        }
      }
      catch (Exception ex)
      {
        cx.Writer.WriteLine("// {0}", ex.Message);
      }

      cx.Writer.WriteLine("}");
    }
    #endregion

    #region ValidationCallback(object sender, ValidationEventArgs args)
    private void ValidationCallback(object sender, ValidationEventArgs args)
    {
      // if (args.Severity == XmlSeverityType.Warning)
      // Console.Write("WARNING: ");
      // else if (args.Severity == XmlSeverityType.Error)
      // Console.Write("ERROR: ");

      // Console.WriteLine(args.Message);
    }
    #endregion
  }
}
